/** @jsxImportSource react */

import _ from 'lodash';
import { cn } from '../../cn';
import { type AstNode, parse, findDefinitions } from '~/common/markdown';
import Highlight from './Highlight';
import { useEffect, useMemo, useState } from 'react';
import { Textarea } from '../shadcn/ui/textarea';
import { BehaviorSubject, debounceTime } from 'rxjs';

function MarkdownChildren({ nodes, definitions }: { nodes?: AstNode[]; definitions: Record<string, AstNode> }) {
  if (_.isEmpty(nodes)) return null;

  return (
    <>
      {_.map(nodes, (n, i) => (
        <MarkdownNode key={i} node={n} definitions={definitions} />
      ))}
    </>
  );
}

function MarkdownNode({ node, definitions }: { node: AstNode; definitions: Record<string, AstNode> }) {
  if (node.type === 'root') {
    return (
      <article>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </article>
    );
  }

  if (node.type === 'blockquote') {
    return (
      <blockquote className="border border-l-8 border-kq-300 rounded px-4 py-2 italic">
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </blockquote>
    );
  }

  if (node.type === 'code') {
    return <Highlight code={node.value || ''} lang={node.lang} />;
  }

  if (node.type === 'definition') {
    return null;
  }

  if (node.type === 'delete') {
    return (
      <del>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </del>
    );
  }

  if (node.type === 'emphasis') {
    return (
      <em>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </em>
    );
  }

  if (node.type === 'heading') {
    const depth = node.depth;
    switch (depth) {
      case 1: {
        return (
          <h1 className="text-3xl font-extrabold mt-8 mb-4">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h1>
        );
      }
      case 2: {
        return (
          <h2 className="text-2xl font-bold mt-6 mb-3">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h2>
        );
      }
      case 3: {
        return (
          <h3 className="text-xl font-bold my-4">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h3>
        );
      }
      case 4: {
        return (
          <h4 className="text-base font-bold my-4">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h4>
        );
      }
      case 5: {
        return (
          <h5 className="text-sm font-bold my-4">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h5>
        );
      }
      case 6: {
        return (
          <h6 className="text-xs font-bold my-2">
            <MarkdownChildren nodes={node.children} definitions={definitions} />
          </h6>
        );
      }
    }
  }

  if (node.type === 'html') {
    return <div>⚠️暂不支持 HTML</div>;
  }

  if (node.type === 'image') {
    // eslint-disable-next-line qwik/jsx-img
    return <img className="only:w-auto only:h-auto only:mx-auto" title={node.title} src={node.url} alt={node.alt} />;
  }

  if (node.type === 'inlineCode') {
    return <code className="border border-kq-200 bg-kq-100 px-2 py-1 text-sm rounded">{node.value}</code>;
  }

  if (node.type === 'link') {
    return (
      <a href={node.url} title={node.title} className="border border-kq-200 rounded" style={{ maxWidth: '100%' }}>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </a>
    );
  }

  if (node.type === 'linkReference') {
    const def = definitions[node.identifier || ''];
    return (
      <a href={def.url || '#'} className="underline">
        <MarkdownChildren nodes={node.children} definitions={definitions} />
        <sup>[{def.__no}]</sup>
      </a>
    );
  }

  if (node.type === 'list') {
    if (node.ordered) {
      return (
        <ol className="list-decimal ml-5">
          <MarkdownChildren nodes={node.children} definitions={definitions} />
        </ol>
      );
    } else {
      return (
        <ul className="list-disc ml-5">
          <MarkdownChildren nodes={node.children} definitions={definitions} />
        </ul>
      );
    }
  }

  if (node.type === 'listItem') {
    if (node.checked === true) {
      return (
        <li style={{ listStyleType: 'checked-item' }}>
          <MarkdownChildren nodes={node.children} definitions={definitions} />
        </li>
      );
    }
    if (node.checked === false) {
      return (
        <li style={{ listStyleType: 'unchecked-item' }}>
          <MarkdownChildren nodes={node.children} definitions={definitions} />
        </li>
      );
    }
    return (
      <li>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </li>
    );
  }

  if (node.type === 'paragraph') {
    return (
      <div className="my-2">
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </div>
    );
  }

  if (node.type === 'strong') {
    return (
      <strong>
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </strong>
    );
  }

  if (node.type === 'table') {
    return (
      <table className="border border-kq-200">
        <tbody>
          <MarkdownChildren nodes={node.children} definitions={definitions} />
        </tbody>
      </table>
    );
  }

  if (node.type === 'tableRow') {
    return (
      <tr className="border-t first:border-t-0 border-kq-200">
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </tr>
    );
  }

  if (node.type === 'tableCell') {
    return (
      <td className="border-l first:border-l-0 border-kq-200 px-2 py-1">
        <MarkdownChildren nodes={node.children} definitions={definitions} />
      </td>
    );
  }

  if (node.type === 'text') {
    return node.value;
  }

  if (node.type === 'thematicBreak') {
    return <hr />;
  }

  return <div className="whitespace-pre">{JSON.stringify(node, null, '  ')}</div>;
}

function MarkdownRender({ node, definitions }: { node: AstNode; definitions: AstNode[] }) {
  const definitionMap: Record<string, AstNode> = {};
  _.forEach(definitions, (node, index) => {
    node.__no = index + 1;
    if (node.url) {
      node.url = _.trim(node.url, ' \t\n\r"\'');
    }
    definitionMap[node.identifier || ''] = node;
  });

  return (
    <>
      <MarkdownNode node={node} definitions={definitionMap} />
      {_.isEmpty(definitions) ? null : (
        <>
          <hr />
          <div className="font-bold text-kq-900 mt-4">参考</div>
          <ol className="list-decimal ">
            {definitions.map((d) => (
              <li className="ml-6" key={d.__no || 0}>
                <a href={d.url}>{d.title}</a>
              </li>
            ))}
          </ol>
        </>
      )}
    </>
  );
}

type MarkdownProps = {
  value: string;
  className?: string | undefined;
};

export default function Markdown(props: MarkdownProps) {
  const { value } = props;

  const root = parse(value, { unposition: true });
  const definitions = findDefinitions(root);

  return (
    <div className={cn('leading-relaxed text-kq-900', props.className)}>
      <MarkdownRender node={root} definitions={definitions} />
    </div>
  );
}

type MarkdownInputProps = {
  value?: string | undefined;
  onChange: (value: string) => void;
  style?: React.CSSProperties | undefined;
  mode: '10' | '11' | '01';
};
export function MarkdownInput({ value, style, onChange, mode }: MarkdownInputProps) {
  const [text, setText] = useState<string>(value || '');
  const [renderText, setRenderText] = useState<string>(text);
  const renderSubject = useMemo(() => new BehaviorSubject<string>(text), []);

  useEffect(() => {
    const subscription = renderSubject
      .pipe(
        debounceTime(300) // delay
      )
      .subscribe((it) => {
        setRenderText(it);
      });

    return () => subscription.unsubscribe();
  }, []);

  return (
    <div>
      <div className="flex gap-1">
        <Textarea
          style={style}
          value={value}
          className={cn(mode == '01' && 'hidden', 'flex-1 border border-kq-200 p-4 font-mono text-sm resize-none')}
          onChange={(e) => {
            const { value } = e.target;
            onChange(value);
            setText(value);
            renderSubject.next(value);
          }}
        />
        <Markdown
          className={cn(mode == '10' && 'hidden', 'flex-1 border border-kq-200 min-w-0 px-4 py-2')}
          value={renderText}
        />
      </div>
    </div>
  );
}
